using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class AttributeSet {
#region Members
	/// <summary>
	/// Son los atributos principales.
	/// </summary>
	Dictionary<ATTRIBUTES, Attribute> attributes;
	/// <summary>
	/// Atributos secundarios, derivados de uno o varios principales. 
	/// Algunos pueden ser independientes tambien y no sumar nada.
	/// </summary>
	Dictionary<SUB_ATTRIBUTES, SubAttributte> subAttributes;
	/// <summary>
	/// Los puntos disponibles para comprar Stats
	/// </summary>
	private int availablePoints;
	//private string characterName = "";
#endregion
#region Constructors	
	/// <summary>
	/// Este constructor inicializa todos los atributos Base en 0.
	/// Los atributos secundarios traen un valor por default, pero lo ideal sería llamar
	/// al SetSecondaryAttributesInitialValues() para darles el valor correcto.
	/// </summary>
	public AttributeSet() : this(0,0,0,0,0,100){}
	/// <summary>
	/// Crea los atributos principales recibe los valores base de todos.
	/// Los atributos secundarios traen un valor por default, pero lo ideal sería llamar
	/// al SetSecondaryAttributesInitialValues() para darles el valor correcto.
	/// </summary>
	/// <param name='STRbase'>
	/// STRENGHT base.
	/// </param>
	/// <param name='DEXbase'>
	/// DEXTERITY base.
	/// </param>
	/// <param name='CONbase'>
	/// CONSTITUTION base.
	/// </param>
	/// <param name='TECbase'>
	/// TECHNIQUE base.
	/// </param>
	/// <param name='AGIbase'>
	/// AGILITY base.
	/// </param>
	public AttributeSet(float STRbase, float DEXbase, float CONbase, 
		float TECbase, float AGIbase, int attributePoints){
		
		attributes = new Dictionary<ATTRIBUTES, Attribute>();
		
		attributes.Add(ATTRIBUTES.AGILITY, new Attribute(ATTRIBUTES.AGILITY,AGIbase));
		attributes.Add(ATTRIBUTES.CONSTITUTION, new Attribute(ATTRIBUTES.CONSTITUTION,CONbase));
		attributes.Add(ATTRIBUTES.DEXTERITY, new Attribute(ATTRIBUTES.DEXTERITY,DEXbase));
		attributes.Add(ATTRIBUTES.STRENGTH, new Attribute(ATTRIBUTES.STRENGTH,STRbase));
		attributes.Add(ATTRIBUTES.TECHNIQUE, new Attribute(ATTRIBUTES.TECHNIQUE,TECbase));
		
		InitializeSecondaryAttributes();
		availablePoints = attributePoints;
	}
#endregion
#region AttributesManagement
/*	public void SetName(string name){
		characterName = name;
		AttributesController.GetInstance().UpdateStats();
	}*/
	
	//public string GetName(){return characterName;}
	/// <summary>
	/// Aumenta el valor base del atributo, se usa al subir de nivel por ejemplo.
	/// </summary>
	/// <param name='attribute'>
	/// Attributo a modificar.
	/// </param>
	/// <param name='ammount'>
	/// Cantidad a aumentar, si pasas negativo le resta.
	/// </param>
	public void RaiseAttributeBaseValue(ATTRIBUTES attribute, float ammount){
		attributes[attribute].RaiseBaseValue(ammount);	
		UpdateSubAttributesValue();
	}
	/// <summary>
	/// Este metodo es similar al Raise, pero se usa para controlar si hay puntos
	/// disponibles antes de aumentar o disminuir el atributo
	/// </summary>
	/// <returns>
	/// Devuelve true si se pudo realizar la operacion
	/// </returns>
	/// <param name='attribute'>
	/// El atributo q se quiere modificar
	/// </param>
	/// <param name='raise'>
	/// True indica aumento del atributo, false es decrecimiento y devuelve puntos.
	/// </param>
	public bool BuyAttributePoint(ATTRIBUTES attribute, bool raise){
		if (raise){
			if (availablePoints >= 1){
				attributes[attribute].RaiseBaseValue(1);
				availablePoints--;
				Debug.Log("Bought, current Points = " + availablePoints);
				UpdateSubAttributesValue();
				AttributesController.GetInstance().UpdateStats();
				CharacterDataManager.GetInstance().GetCurrentCharData()
					.perks.RefreshPerks();
				return true;
			}
			Debug.Log("Not bought, current Points = " + availablePoints);
			return false;
		}
		else{
			if (attributes[attribute].GetBaseValue() > 1){
				attributes[attribute].RaiseBaseValue(-1);
				availablePoints++;
				UpdateSubAttributesValue();
				AttributesController.GetInstance().UpdateStats();
				CharacterDataManager.GetInstance().GetCurrentCharData()
					.perks.RefreshPerks();
				return true;
			}
			return false;
		}		
	}
	public float GetAttributeBaseValue(ATTRIBUTES attribute){
		return attributes[attribute].GetBaseValue();	
	}
	/// <summary>
	/// Agrega la cantidad al bonificador temporal de este estatus, funcionaría como un Buff.
	/// </summary>
	/// <param name='attribute'>
	/// Attributo a modificar.
	/// </param>
	/// <param name='bonus'>
	/// Cantidad a sumar.
	/// </param>
	public void AddBonusToAttribute(ATTRIBUTES attribute, float bonus){
		attributes[attribute].AddToBonus(bonus);
		UpdateSubAttributesValue();
	}
	/// <summary>
	/// Aumenta el porcentaje de bonus que se calcula para sacar el total del atributo.
	/// Calculo (Base + bonus) + porcentageBonus = Total.
	/// </summary>
	/// <param name='ammount'>
	/// La cantidad chango.
	/// </param>
	public void AddPercetageBonusToAttribute(ATTRIBUTES attribute, float ammount){
		attributes[attribute].AddToPercetageBonus(ammount);
		UpdateSubAttributesValue();
	}
	public float GetAttributeBonusValue(ATTRIBUTES attribute){
		return attributes[attribute].GetBonusValue();	
	}
	public float GetAttributePercentageBonus(ATTRIBUTES att){
		return attributes[att].GetPercentageBonus();	
	}
	/// <summary>
	/// El valor total es la suma del base y los bonus activos.
	/// </summary>
	/// <param name='attribute'>
	/// Attributo del cual se pide el valor total.
	/// </param>
	public float GetAttributeTotalValue(ATTRIBUTES attribute){
		return attributes[attribute].GetTotalValue();	
	}
	public float[] GetAttributeDataArray(ATTRIBUTES att){
		return attributes[att].ToArrayData();	
	}
	public void SetAttributeDataArray(ATTRIBUTES att, float[] dataArray){
		attributes[att].SetArrayData(dataArray);	
	}
#endregion
#region SubAttributesManagement
	public float GetSubAttributeBaseValue(SUB_ATTRIBUTES subAtt){
		return subAttributes[subAtt].GetBaseValue();
	}
	/// <summary>
	/// Agrega la cantidad al bonificador temporal de este Atributo Secundario.
	/// El valor base no cambia nunca en estos, o sea que siempre se usa este método 
	/// para cualquier Perk o Item que aumente alguno de esos sub atributos.
	/// </summary>
	/// <param name='attribute'>
	/// Sub Attributo a modificar.
	/// </param>
	/// <param name='bonus'>
	/// Cantidad a sumar.
	/// </param>
	public void AddBonusToSubAttribute(SUB_ATTRIBUTES subAtt, float bonus){
		subAttributes[subAtt].AddToBonus(bonus);
	}
	public float GetSubAttributeBonusValue(SUB_ATTRIBUTES subAtt){
		return subAttributes[subAtt].GetBonusValue();	
	}
	/// <summary>
	/// El valor total es la suma del base y los bonus activos.
	/// </summary>
	/// <param name='attribute'>
	/// Sub Attributo del cual se pide el valor total.
	/// </param>
	public float GetSubAttributeMaxValue(SUB_ATTRIBUTES subAttrib){
		return subAttributes[subAttrib].GetTotalValue();	
	}
	/// <summary>
	/// Este método suma el valor recibido al valor actual, se usa para modificar los
	/// Sub Atributos que cambian su valor actual durante el juego, por ejemplo HP,Stamina
	/// </summary>
	/// <param name='subAtt'>
	/// Sub atributo a modificar
	/// </param>
	/// <param name='ammount'>
	/// Cantidad a sumar, si es negativo se resta.
	/// </param>
	public void ModifySubAttributeActualValue(SUB_ATTRIBUTES subAtt, float ammount){
		subAttributes[subAtt].AddToActualValue(ammount);
	}
	public float GetSubAttributeActualValue(SUB_ATTRIBUTES subAttrib){ 
		return subAttributes[subAttrib].GetActualValue();
	}
	/// <summary>
	/// Aumenta el porcentaje de bonus que se calcula para sacar el total del subatributo.
	/// Calculo (Base + bonus + dependencia) + porcentageBonus = Total.
	/// </summary>
	/// <param name='ammount'>
	/// La cantidad chango.
	/// </param>
	public void AddPercetageBonusToSubAttribute(SUB_ATTRIBUTES subAttribute, float ammount){
		subAttributes[subAttribute].AddToPercetageBonus(ammount);
		UpdateSubAttributesValue();
	}
	public float GetSubAttributePercentageBonus(SUB_ATTRIBUTES subAtt){
		return subAttributes[subAtt].GetPercentageBonus();	
	}
	/// <summary>
	/// Setea los valores iniciales base de los atributos secundarios. Por defecto 
	/// tienen los valores de SubAttributes.defaultValues[] pero si se quieren cambiar este 
	/// método es para eso.
	/// </summary>
	public void SetSecondaryAttributesInitialValues(float inventorySlots, float movementSpeed,
		float hitPoints, float reloadTime, float noiseFactor, float stamina){
		subAttributes[SUB_ATTRIBUTES.HIT_POINTS].SetInitialValue(hitPoints);
		subAttributes[SUB_ATTRIBUTES.INVENTORY_SLOTS].SetInitialValue(inventorySlots);
		subAttributes[SUB_ATTRIBUTES.MOVEMENT_SPEED].SetInitialValue(movementSpeed);
		subAttributes[SUB_ATTRIBUTES.NOISE_FACTOR].SetInitialValue(noiseFactor);
		subAttributes[SUB_ATTRIBUTES.EQUIP_TIME].SetInitialValue(reloadTime);
		subAttributes[SUB_ATTRIBUTES.STAMINA].SetInitialValue(stamina);
	}
	/// <summary>
	/// Devuelve un float con los bonus por dependencias que tiene el subatributo
	/// consultado.
	/// </summary>
	/// <param name='subAtt'>
	/// Sub atributo que se quire consultar.
	/// </param>
	public float GetDependenciesBonusOfSubAttribute(SUB_ATTRIBUTES subAtt){
		return subAttributes[subAtt].GetDependenciesBonus();	
	}
	/// <summary>
	/// Este método se usa para decirle a un sub atributo si suma los valores del atributo
	/// base del cual depende o no. Hay Perks que modifican esto como el FastManeuvers.
	/// </summary>
	/// <param name='subAtt'>
	/// Sub atributo para modificar.
	/// </param>
	/// <param name='val'>
	/// Un simple Booleano: true o false, si o no, 1 o 0, blanco o negro, bien o mal, luz u oscuridad.
	/// </param>
	public void HasToApplyDependenciesBonus(SUB_ATTRIBUTES subAtt, bool val){
		subAttributes[subAtt].ApplyDependenciesBonus(val);
	}
	public float[] GetSubAttributeDataArray(SUB_ATTRIBUTES subAtt){
		return subAttributes[subAtt].ToArrayData();	
	}
	public void SetSubAttributeDataArray(SUB_ATTRIBUTES subAtt, float[] dataArray){
		subAttributes[subAtt].SetArrayData(dataArray);	
	}
	public void RefillValues(){
		foreach(KeyValuePair<SUB_ATTRIBUTES, SubAttributte> sa in subAttributes){
			sa.Value.Refill();	
		}
	}
#endregion
	
	public void AddAttributePoints(int ammount){availablePoints += ammount;}
	public void SetAttributePoints(int ammount){availablePoints = ammount;}
	public int GetAttributePoints(){return this.availablePoints;}
	
	/// <summary>
	/// Este método se llama desde el constructor, y lo que hace es crear los atributos
	/// secundarios, darles un valor base default y setearles las dependencias.
	/// Para cambiar dependencias o el factor que multiplica hacerlo en este método.
	/// </summary>
	private void InitializeSecondaryAttributes(){
		subAttributes = new Dictionary<SUB_ATTRIBUTES, SubAttributte>();
		
		SubAttributte HP = new SubAttributte(SUB_ATTRIBUTES.HIT_POINTS);
		SubAttributte InventorySlots = new SubAttributte(SUB_ATTRIBUTES.INVENTORY_SLOTS);
		SubAttributte MovementSpeed = new SubAttributte(SUB_ATTRIBUTES.MOVEMENT_SPEED);
		SubAttributte NoiseFactor = new SubAttributte(SUB_ATTRIBUTES.NOISE_FACTOR);
		SubAttributte ReloadTime = new SubAttributte(SUB_ATTRIBUTES.EQUIP_TIME);
		SubAttributte Stamina = new SubAttributte(SUB_ATTRIBUTES.STAMINA);
		
		HP.SetDependencies( new DependencyPair(attributes[ATTRIBUTES.CONSTITUTION], 5));
		MovementSpeed.SetDependencies(new DependencyPair(attributes[ATTRIBUTES.AGILITY], 0.02f));
		NoiseFactor.SetDependencies(new DependencyPair(attributes[ATTRIBUTES.TECHNIQUE], -0.25f));
		Stamina.SetDependencies(new DependencyPair(attributes[ATTRIBUTES.CONSTITUTION], 1));
		ReloadTime.SetDependencies(new DependencyPair(attributes[ATTRIBUTES.AGILITY],-0.020f));
		ReloadTime.ApplyDependenciesBonus(false);
		
		subAttributes.Add(SUB_ATTRIBUTES.HIT_POINTS, HP);
		subAttributes.Add(SUB_ATTRIBUTES.INVENTORY_SLOTS, InventorySlots);
		subAttributes.Add(SUB_ATTRIBUTES.MOVEMENT_SPEED, MovementSpeed);
		subAttributes.Add(SUB_ATTRIBUTES.NOISE_FACTOR, NoiseFactor);
		subAttributes.Add(SUB_ATTRIBUTES.EQUIP_TIME, ReloadTime);
		subAttributes.Add(SUB_ATTRIBUTES.STAMINA, Stamina);
	}	
	public void UpdateSubAttributesValue(){
		foreach(KeyValuePair<SUB_ATTRIBUTES, SubAttributte> subAttrib in subAttributes){
			subAttrib.Value.UpdateMaxValue();	
		}
	}
	public void LoadValues(CharacterInfo charInfo){
		//attributes[ATTRIBUTES.AGILITY].SetArrayData
	}
}
